package com.econ.powercloud.ems.service.biz;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.econ.powercloud.common.base.facade.enums.StatusType;
import com.econ.powercloud.common.base.facade.exception.CommonRuntimeException;
import com.econ.powercloud.common.base.facade.pojo.MsgResult;
import com.econ.powercloud.common.base.facade.pojo.PageResult;
import com.econ.powercloud.common.base.facade.util.JacksonUtil;
import com.econ.powercloud.ems.common.enums.*;
import com.econ.powercloud.ems.common.util.IdWorker;
import com.econ.powercloud.ems.config.InitParameter;
import com.econ.powercloud.ems.constant.SysConstant;
import com.econ.powercloud.ems.dal.entity.*;
import com.econ.powercloud.ems.domain.EmsEnergyEquipmentDO;
import com.econ.powercloud.ems.domain.EmsEnergyStationDO;
import com.econ.powercloud.ems.pojo.ApiDataVO;
import com.econ.powercloud.ems.pojo.EmsClusterConfigVO;
import com.econ.powercloud.ems.pojo.EmsEnergyEquipmentVO;
import com.econ.powercloud.ems.pojo.EmsEnergyStationVO;
import com.econ.powercloud.ems.pojo.requestVO.EmsEnergyStationEdit;
import com.econ.powercloud.ems.pojo.requestVO.EmsEnergyStationRequestVO;
import com.econ.powercloud.ems.pojo.requestVO.EmsStationSvgEdit;
import com.econ.powercloud.ems.pojo.scene.OpenApiSceneStructureVO;
import com.econ.powercloud.ems.service.dao.*;
import com.econ.powercloud.ems.service.biz.scene.SceneBizService;
import com.econ.powercloud.ems.util.DoubleUtil;
import com.econ.powercloud.powernode.gateway.common.domain.UserDTO;
import com.google.common.collect.Lists;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @Author : zjf
 * @Description : 储能站service
 * @Date : 2021/7/28 17:38
 * @Modified by : zjf
 */
@Component
public class EnergyStationService {

    public static final Integer SCENE_PUBLISH = 2;

    @Autowired
    private EmsEnergyStationDAO emsEnergyStationDAO;
    @Autowired
    private EmsEnergyEquipmentDAO emsEnergyEquipmentDAO;
    @Autowired
    private CompanyUtilService companyUtilService;
    @Autowired
    private EmsSyncIotProductDAO emsSyncIotProductDAO;
    @Autowired
    private IdWorker idWorker;
    @Autowired
    private EnergyScheduleService energyScheduleService;
    @Autowired
    private EmsDeviceDAO emsDeviceDAO;
    @Autowired
    private SynchronousSceneDataService synchronousSceneDataService;
    @Autowired
    private EmsAreaDAO emsAreaDAO;
    @Autowired
    private EmsClusterConfigDAO emsClusterConfigDAO;
    @Autowired
    private EmsClusterModelConfigDAO emsClusterModelConfigDAO;
    @Autowired
    private InitParameter initParameter;
    @Autowired
    private ExtIotSceneDAO extIotSceneDAO;
    @Autowired
    private SceneBizService sceneBizService;
    @Autowired
    private AuthCompanyDAO authCompanyDAO;
    @Autowired
    private DeviceService deviceService;

    /**
     * 获取单个储能站的基本信息
     * @param energyStationId
     * @return
     */
    public EmsEnergyStationVO getEnergyStationById(String energyStationId){
        EmsEnergyStation emsEnergyStation = emsEnergyStationDAO.getBaseMapper().selectById(energyStationId);
        EmsEnergyStationVO energyStationVO = EmsEnergyStationDO.entityToVO(emsEnergyStation);
        this.initEnergyStationOtherInfo(Collections.singletonList(energyStationVO));
        return energyStationVO;
    }

    public void initEnergyStationOtherInfo(List<EmsEnergyStationVO> stationList){
        //查询省市名称
        Map<Integer,String> areaMap = emsAreaDAO.list().stream().collect(Collectors.toMap(EmsArea::getCityCode, EmsArea::getCityName));
        //查询公司名称
        Set<String> companyIds = stationList.stream().map(EmsEnergyStationVO::getCompanyId).collect(Collectors.toSet());
        companyIds.addAll(stationList.stream().map(EmsEnergyStationVO::getMaintenanceCompany).collect(Collectors.toSet()));
        //获取公司相关信息
        List<AuthCompany> authCompanyList = authCompanyDAO.list();
        Map<String,String> companyMap = authCompanyList.stream().collect(Collectors.toMap(AuthCompany::getPid,AuthCompany::getName));
        //查询储能站下的簇的分布情况
        List<EmsDevice> clusterList = emsDeviceDAO.selectByStationIdsAndDeviceTypes(stationList.stream().map(EmsEnergyStationVO::getEnergyStationId).collect(Collectors.toList()), Lists.newArrayList(DeviceType.CLUSTER.getCode()));
        Map<String,List<EmsDevice>> stationClusterMap = clusterList.stream().collect(Collectors.groupingBy(d->d.getEnergyStationId()));
        Map<String, EmsClusterConfig> clusterConfigMap = emsClusterConfigDAO.selectByDeviceCodes(clusterList.stream().map(EmsDevice::getDeviceCode).collect(Collectors.toList()))
                .stream().collect(Collectors.toMap(EmsClusterConfig::getDeviceCode, d->d));
        Map<Integer,String> modelConfigMap = emsClusterModelConfigDAO.list().stream().collect(Collectors.toMap(EmsClusterModelConfig::getId, EmsClusterModelConfig::getModelName));
        //赋值操作
        stationList.stream().forEach(d->{
            d.setCompanyName(companyMap.get(d.getCompanyId()));
            d.setProvinceName(areaMap.get(d.getProvince()));
            d.setCityName(areaMap.get(d.getCity()));
            d.setVoltageLevelName(VoltageLevel.getDescFromCode(d.getVoltageLevel()));
            d.setPriceTypeName(PriceType.getDescFromCode(d.getPriceType()));
            d.setStationTypeName(StationType.getCodeFromCode(d.getStationType()));
            d.setMaintenanceCompanyName(d.getMaintenanceCompany() == null?null :companyMap.get(d.getMaintenanceCompany()));
            d.setOperateStatusName(StationOperateStatusType.getCodeFromCode(d.getOperateStatus()));
            d.setShareRatio(DoubleUtil.get2Point(d.getShareRatio() * 100));
            List<EmsClusterConfigVO> clusterConfigVOS = Lists.newArrayList();
            stationClusterMap.get(d.getEnergyStationId()).stream().forEach(device ->{
                EmsClusterConfig emsClusterConfig = clusterConfigMap.get(device.getDeviceCode());
                clusterConfigVOS.add(EmsClusterConfigVO.builder()
                        .id(emsClusterConfig.getId()).deviceCode(emsClusterConfig.getDeviceCode())
                        .deviceName(device.getDeviceName()).type(emsClusterConfig.getType())
                        .moduleName(modelConfigMap.get(emsClusterConfig.getType()))
                        .build());
            });
            d.setClusterConfigVOS(clusterConfigVOS);
        });
    }

    /**
     * 获取储能站列表信息
     * @param pageNo
     * @param pageSize
     * @return
     */
    public PageResult<List<EmsEnergyStationVO>> getList(UserDTO userInfo, Integer pageNo, Integer pageSize){
        //通过用户获取所能查看的公司有哪些
        List<String> companyIds = companyUtilService.getCompanyIdsByRole(userInfo);
        Page<EmsEnergyStation> page = emsEnergyStationDAO.selectListByCondition(companyIds,null,pageNo,pageSize);
        List<EmsEnergyStationVO> energyStationVOList = EmsEnergyStationDO.entityListToVOList(page.getRecords());
        if(CollectionUtils.isNotEmpty(energyStationVOList)){
            //查询储能装置有哪些
            List<String> stationIds = energyStationVOList.stream().map(EmsEnergyStationVO::getEnergyStationId).collect(Collectors.toList());
            List<EmsEnergyEquipment> energyEquipmentList = emsEnergyEquipmentDAO.selectByStationIds(stationIds);
            Map<String,List<EmsEnergyEquipment>> equipmentMap = energyEquipmentList.stream().collect(Collectors.groupingBy(d->d.getEnergyStationId()));
            energyStationVOList.stream().forEach(d->{
                d.setShareRatio(d.getShareRatio() * 100);
                if(CollectionUtils.isNotEmpty(equipmentMap.get(d.getEnergyStationId()))){
                    List<EmsEnergyEquipmentVO> list = EmsEnergyEquipmentDO.entityListToVOList(equipmentMap.get(d.getEnergyStationId()));
                    d.setEquipmentVOList(list);
                }
            });
        }
        return PageResult.<List<EmsEnergyStationVO>>builder().data(energyStationVOList)
                .pageNo((int)page.getCurrent()).totalCount((int)page.getTotal()).build();
    }

    public PageResult<List<ApiDataVO>> getEnergyStationListByCondition(UserDTO user, String searchName, Integer pageNo, Integer pageSize) {
        List<String> companyIds = companyUtilService.getCompanyIdsByRole(user);
        Page<EmsEnergyStation> emsEnergyStationPage = emsEnergyStationDAO.selectListByCondition(companyIds, searchName, pageNo, pageSize);
        List<ApiDataVO> apiDataVOS = Lists.newArrayList();
        for (EmsEnergyStation emsEnergyStation : emsEnergyStationPage.getRecords()) {
            apiDataVOS.add(ApiDataVO.builder().code(emsEnergyStation.getEnergyStationId()).name(emsEnergyStation.getEnergyStationName()).build());
        }
        return PageResult.<List<ApiDataVO>>builder().data(apiDataVOS)
                .pageNo((int)emsEnergyStationPage.getCurrent()).totalCount((int)emsEnergyStationPage.getTotal()).build();
    }

    /**
     * 检验场景编号对应的结构图是否正确
     */
    public List<ApiDataVO> checkSceneCode(String sceneCode){
        //查看场景服务是否已被使用
        List<String> useSceneCodes = emsEnergyStationDAO.list().stream().map(EmsEnergyStation::getSceneCode).collect(Collectors.toList());
        if(CollectionUtils.isNotEmpty(useSceneCodes) && useSceneCodes.contains(sceneCode)){
            throw new CommonRuntimeException(StatusType.FAIL,"该场景编号已被使用！");
        }
        //调用场景服务的接口，获取该场景的基本信息
        ExtIotScene scene = extIotSceneDAO.selectBySceneCodeAndApplicationCode(sceneCode, SysConstant.EMS_APPLICATION);
        Assert.notNull(scene, "场景不存在！");
        if(!SysConstant.EMS_APPLICATION.equals(scene.getApplicationCode())){
            throw new CommonRuntimeException(StatusType.FAIL, "场景对应的应用错误!");
        }
        if(!SCENE_PUBLISH.equals(scene.getStatus())){
            throw new CommonRuntimeException(StatusType.FAIL, "场景未发布");
        }
        //调用场景服务的接口,获取该场景的结构图信息
        List<OpenApiSceneStructureVO> sceneStructureList = sceneBizService.getSceneStructure(sceneCode, SysConstant.EMS_APPLICATION);
        if (CollectionUtils.isEmpty(sceneStructureList) || sceneStructureList.size() != 1) {
            throw new CommonRuntimeException(StatusType.FAIL, "结构异常!");
        }
        OpenApiSceneStructureVO structure = sceneStructureList.get(0);
        if (!DeviceType.STATION.getCodeInScene().equals(structure.getDeviceTypeCode())) {
            throw new CommonRuntimeException(StatusType.FAIL, "结构异常!");
        }
        //获取该场景下的节点列表
        List<OpenApiSceneStructureVO> structureVOList = Lists.newArrayList();
        this.getSceneStructureList(structure,structureVOList);
        //校验该场景下的产品是否都已在EMS中同步过
        Set<String> productKeys = structureVOList.stream().filter(d->d.getProductKey() != null).map(OpenApiSceneStructureVO::getProductKey).collect(Collectors.toSet());
        if(CollectionUtils.isEmpty(productKeys)){
            throw new CommonRuntimeException(StatusType.FAIL,"结构异常!");
        }
        List<EmsSyncIotProduct> productSyncs = emsSyncIotProductDAO.selectByProductKeys(Lists.newArrayList(productKeys));
        if(productSyncs.size() < productKeys.size()){
            throw new CommonRuntimeException(StatusType.FAIL,"产品缺失，无法同步，请先同步产品");
        }
        //按照设备类型分组,在获取类型为BMU的数据
        Map<String,List<OpenApiSceneStructureVO>> structureMap = structureVOList.stream().collect(Collectors.groupingBy(d->d.getDeviceTypeCode()));
        List<OpenApiSceneStructureVO> clusterStructureList = structureMap.get(DeviceType.CLUSTER.getCodeInScene());
        if(CollectionUtils.isEmpty(clusterStructureList)){
            throw new CommonRuntimeException(StatusType.FAIL, "结构异常!");
        }
        //组装返回结果,code: deviceCode + . + moduleCode
        List<ApiDataVO> result = Lists.newArrayList();
        clusterStructureList.stream().forEach(d-> result.add(ApiDataVO.builder().code(d.getNodeId()).name(d.getNodeName()).build()));
        return result;
    }

    public void getSceneStructureList(OpenApiSceneStructureVO structure,List<OpenApiSceneStructureVO> sceneStructureVOList){
        sceneStructureVOList.add(structure);
        if(CollectionUtils.isEmpty(structure.getChildData())){
            return;
        }
        structure.getChildData().stream().forEach(d->{
            this.getSceneStructureList(d,sceneStructureVOList);
        });
    }

    /**
     * 新增储能站
     * @param requestVO : 请求VO类
     * 这里的同步接口大部分不在校验，因为在新增储能站之前需要进行场景的校验操作
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public String addStation(EmsEnergyStationRequestVO requestVO){
        //校验储能站名称是否重复
        List<String> stationName = emsEnergyStationDAO.list().stream().map(EmsEnergyStation::getEnergyStationName).collect(Collectors.toList());
        if(stationName.contains(requestVO.getEnergyStationName())){
            throw new CommonRuntimeException(StatusType.FAIL,"储能站名称重复");
        }
        List<EmsDevice> insertDeviceData = Lists.newArrayList();
        List<EmsEnergyEquipment> insertEquipmentData = Lists.newArrayList();
        /**
         * 组装储能站基本信息，涉及的表ems_energy_station
         */
        String stationId = idWorker.nextId()+"";
        EmsEnergyStation emsEnergyStation = EmsEnergyStation.builder()
                .energyStationId(stationId).energyStationName(requestVO.getEnergyStationName())
                .companyId(requestVO.getCompanyId()).province(requestVO.getProvince()).city(requestVO.getCity())
                .voltageLevel(requestVO.getVoltageLevel()).stationType(requestVO.getStationType())
                .maxDemand(requestVO.getMaxDemand()).transformCapacity(requestVO.getTransformCapacity())
                .priceType(requestVO.getPriceType()).energyStationAddress(requestVO.getEnergyStationAddress())
                .industryType(1).commissioningDate(System.currentTimeMillis()).configurationCapacity(requestVO.getConfigurationCapacity())
                .shareRatio(DoubleUtil.get4Point(requestVO.getShareRatio()/100))
                .partyA(requestVO.getPartyA()).partyC(requestVO.getPartyC())
                .maintenanceCompany(requestVO.getMaintenanceCompany()).maintenanceUser(requestVO.getMaintenanceUser())
                .maintenancePhone(requestVO.getMaintenancePhone()).sceneCode(requestVO.getSceneCode())
                .temperatureUpper(48.0).temperatureLower(-10.0)
                .svgSource(requestVO.getSvgSource())
                .gmtCreate(System.currentTimeMillis()).gmtModify(System.currentTimeMillis())
                .build();
        /**
         * 由储能站基本信息 与 场景编号同步场景中的结构图信息
         * 涉及的表有 ems_device,ems_energy_equipment,ems_energy_schedule,ems_peak_shift,ems_peak_shift_detail
         */
        synchronousSceneDataService.initEmsDeviceData(stationId,requestVO.getSceneCode(),insertDeviceData,insertEquipmentData);
        //设置储能站的储能装置数量
        emsEnergyStation.setEquipmentCount(insertEquipmentData.size());
        emsEnergyStationDAO.save(emsEnergyStation);
        //这里需要对储能装置的能量调度进行初始化操作
        insertEquipmentData.stream().forEach(d->{
            energyScheduleService.addDefaultEnergySchedule(d.getEnergyStationId(),d.getEnergyEquipmentId());
        });
        /**
         * 同步该储能站(即该场景)下的计算数据,涉及的表 ems_device_cal_config
         */
        synchronousSceneDataService.initEmsDeviceCalConfig(insertDeviceData,requestVO.getSceneCode());
        /**
         * 设置簇的电池pack配置,涉及的表有 ems_cluster_config
         */
        synchronousSceneDataService.initClusterConfigWithPack(insertDeviceData,requestVO.getClusterConfigVOS());
        /**
         * 配置报表中的列配置信息，涉及的表ems_column_config
         */
        synchronousSceneDataService.initEmsColumnConfigInfo(insertDeviceData,insertEquipmentData,requestVO.getStationType());
        //刷新缓存
        initParameter.init();
        //自动集装箱下设置簇位置
        if(Objects.equals(StationType.HIGH_PRESSURE_CASCADE.getCode(),requestVO.getStationType())){
            deviceService.autoClusterSetBatch();
        }
        return emsEnergyStation.getEnergyStationId();
    }

    @Transactional(rollbackFor = Exception.class)
    public void updateStation(EmsEnergyStationEdit requestVO){
        EmsEnergyStation emsEnergyStation = JacksonUtil.convertToObj(requestVO, EmsEnergyStation.class);
        emsEnergyStation.setShareRatio(DoubleUtil.get4Point(requestVO.getShareRatio()/100));
        emsEnergyStation.setGmtModify(System.currentTimeMillis());
        emsEnergyStationDAO.updateById(emsEnergyStation);
        List<EmsClusterConfig> clusterConfigList = JacksonUtil.convertToList(requestVO.getClusterConfigVOS(), EmsClusterConfig.class);
        emsClusterConfigDAO.updateBatchById(clusterConfigList);
    }

    /**
     * 更新储能站的计算数据
     * @param stationId
     */
    @Transactional(rollbackFor = Exception.class)
    public void updateStationCalData(String stationId){
        EmsEnergyStation emsEnergyStation = emsEnergyStationDAO.getById(stationId);
        if(emsEnergyStation == null){
            throw new CommonRuntimeException(StatusType.FAIL,"未找到该储能站！");
        }
        List<EmsDevice> emsDeviceList = emsDeviceDAO.selectByEnergyStationId(stationId);
        if(CollectionUtils.isEmpty(emsDeviceList)){
            throw new CommonRuntimeException(StatusType.FAIL,"该储能站下不存在设备！");
        }
        synchronousSceneDataService.initEmsDeviceCalConfig(emsDeviceList,emsEnergyStation.getSceneCode());
    }

    @Transactional(rollbackFor = Exception.class)
    public void updateSvgSource(EmsStationSvgEdit svgEdit){
        EmsEnergyStation emsEnergyStation = emsEnergyStationDAO.getById(svgEdit.getEnergyStationId());
        Assert.notNull(emsEnergyStation,"未找到该储能站！");
        emsEnergyStation.setSvgSource(svgEdit.getSvgSource());
        emsEnergyStationDAO.updateById(emsEnergyStation);
    }

    @Transactional(rollbackFor = Exception.class)
    public void updateTemperatureRange(String stationId,Double upper,Double lower){
        EmsEnergyStation station = emsEnergyStationDAO.getById(stationId);
        Assert.notNull(station,"未找到该储能站！");
        station.setTemperatureUpper(upper);
        station.setTemperatureLower(lower);
        emsEnergyStationDAO.updateById(station);
    }

    @Transactional(rollbackFor = Exception.class)
    public void updateStationStructure(String stationId){
        EmsEnergyStation station = emsEnergyStationDAO.getById(stationId);
        Assert.notNull(station,"未找到该储能站！");
        List<EmsDevice> insertDeviceData = Lists.newArrayList();
        List<EmsEnergyEquipment> insertEquipmentData = Lists.newArrayList();
        synchronousSceneDataService.updateEmsDeviceData(stationId,station.getSceneCode(),insertDeviceData,insertEquipmentData);
    }
}
